//
//  AACAudioDec.m
//
//  Created by Liusr on 2017/1/14.
//  Copyright © 2017年 Liusr. All rights reserved.
//

#import "AACAudioDec.h"
#include <pthread.h>
#include <AudioToolbox/AudioToolbox.h>

const unsigned int kNumAQBufs = 3;
const size_t kAQBufSize = 128 * 8;
const size_t kAQMaxPacketDescs = 1;
const size_t kRecvBufSize = 1024;

@interface AACAudioDec ()
{
    char *_bufTag;
    bool _queueExist;//标识stream或者queue的状态 不是特别准确 是否改成直接判断queue
    bool _dataPlayOver;
    
    NSTimer *_timer;
    
    AudioFileStreamID _audioFileStream;
    AudioQueueRef _audioQueue;
    AudioQueueBufferRef _audioQueueBuffer[kNumAQBufs];
    AudioStreamPacketDescription _packetDescs[kAQMaxPacketDescs];
    int _fillBufferIndex;
    UInt32 _bytesFilled;
    UInt32 _packetsFilled;
    bool _inuse[kNumAQBufs];
    bool _started;//queue start
    
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;
//    pthread_cond_t _done;//目前没用到结束锁
}
@end

@implementation AACAudioDec

-(instancetype)init{
    if(self = [super init]){
        [self initBuf];
        [self initTimer];
    }
    return self;
}
-(void)initBuf{
    pthread_mutex_init(&_mutex, NULL);
    pthread_cond_init(&_cond, NULL);
    
    _bufTag = (char*)malloc(kRecvBufSize * sizeof(char));
//    memset(_bufTag, 0, sizeof(char) * kRecvBufSize);
}
-(void)initTimer{
    _timer = [NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:@selector(audioqueueReset) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] run];
}
-(void)initAudioFileStream{
    
    NSLog(@"定时器  开始执行一次初始化----");
    [self clearStatus];
    OSStatus err = AudioFileStreamOpen( (__bridge void *)self, MyPropertyListenerProc, MyPacketsProc,kAudioFileAAC_ADTSType, &_audioFileStream);
    if (err) {
        NSLog(@"audiofilestreamOpen error %d",(int)err);
        return ;
    }
    _queueExist = true;
    NSLog(@"定时器   执行一次初始化-------");
    return;
}

void MyPropertyListenerProc( void *inClientData, AudioFileStreamID inAudioFileStream, AudioFileStreamPropertyID inPropertyID, UInt32 *ioFlags)
{
    AACAudioDec *aacDec = (__bridge AACAudioDec *)inClientData;
    [aacDec handleAudioFileStreamPropertyID:inPropertyID];
}

-(void)handleAudioFileStreamPropertyID:(AudioFileStreamPropertyID)inPropertyID{
    OSStatus err = noErr;
    switch (inPropertyID) {
        case kAudioFileStreamProperty_ReadyToProducePackets :
        {
            AudioStreamBasicDescription asbd;
            UInt32 asbdSize = sizeof(asbd);
            err = AudioFileStreamGetProperty(_audioFileStream, kAudioFileStreamProperty_DataFormat, &asbdSize, &asbd);
            if (err) {
                NSLog(@"AudioFileStreamGetProperty err %d",(int)err);
                break;
            }
            
            // create the audio queue
            err = AudioQueueNewOutput(&asbd, MyAudioQueueOutputCallback, (__bridge void *)(self), NULL, NULL, 0, &_audioQueue);
            if (err) {
                NSLog(@"AudioQueueNewOutput err %d",(int)err);
                break;
            }
            
            // allocate audio queue buffers
            for (unsigned int i = 0; i < kNumAQBufs; ++i) {
                err = AudioQueueAllocateBuffer(_audioQueue, kAQBufSize, &_audioQueueBuffer[i]);
                if (err) {
                    NSLog(@"AudioQueueAllocateBuffer err %d",(int)err);
                    break;
                }
            }
            
            // get the cookie size
            UInt32 cookieSize;
            Boolean writable;
            err = AudioFileStreamGetPropertyInfo(_audioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, &writable);
            if (err) {
                NSLog(@"kAudioFileStreamProperty_MagicCookieData %d",(int)err);
                break;
            }
            NSLog(@"cookieSize %d\n", (unsigned int)cookieSize);
            
            // get the cookie data
            void* cookieData = calloc(1, cookieSize);
            err = AudioFileStreamGetProperty(_audioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, cookieData);
            if (err) {
                NSLog(@"kAudioFileStreamProperty_MagicCookieData err %d",(int)err);
                free(cookieData);
                break;
            }
            
            // set the cookie on the queue.
            err = AudioQueueSetProperty(_audioQueue, kAudioQueueProperty_MagicCookie, cookieData, cookieSize);
            free(cookieData);
            cookieData = NULL;
            if (err) {
                NSLog(@"kAudioQueueProperty_MagicCookie err %d",(int)err);
                break;
            }
            
            // listen for kAudioQueueProperty_IsRunning
            
            err = AudioQueueAddPropertyListener(_audioQueue, kAudioQueueProperty_IsRunning, MyAudioQueueIsRunningCallback, (__bridge void *)self);
            if (err) {
                NSLog(@"AudioQueueAddPropertyListener err %d",(int)err);
                break;
            }
        }
            break;
    }
}

-(void)handleAudioPackets:(UInt32)inNumberPackets bytes:(UInt32)inNumberBytes data:(const void *)inInputData description:(AudioStreamPacketDescription *)inPacketDescriptions{
    
    for (int i = 0; i < inNumberPackets; ++i) {
        SInt64 packetOffset = inPacketDescriptions[i].mStartOffset;
        SInt64 packetSize   = inPacketDescriptions[i].mDataByteSize;
        
        size_t bufSpaceRemaining = kAQBufSize - _bytesFilled;
        if (bufSpaceRemaining < packetSize) {//packetsize每次不超600
            [self enQueueBuffer];
            [self waitForFreeBuffer];
        }
        
        AudioQueueBufferRef fillBuf = _audioQueueBuffer[_fillBufferIndex];//引用值还是地址
        
        memcpy((char*)fillBuf->mAudioData + _bytesFilled, (const char*)inInputData + packetOffset, packetSize);

        _packetDescs[_packetsFilled] = inPacketDescriptions[i];
        _packetDescs[_packetsFilled].mStartOffset = _bytesFilled;

        _bytesFilled += packetSize;
        _packetsFilled += 1;
        
        size_t packetsDescsRemaining = kAQMaxPacketDescs - _packetsFilled;
        if (packetsDescsRemaining == 0) {
            [self enQueueBuffer];
            [self waitForFreeBuffer];
        }
    }
}

void MyPacketsProc( void *inClientData, UInt32 inNumberBytes, UInt32 inNumberPackets, const void *inInputData, AudioStreamPacketDescription *inPacketDescriptions)
{
    AACAudioDec *aacDec = (__bridge AACAudioDec *)inClientData;
    [aacDec handleAudioPackets:inNumberPackets bytes:inNumberBytes data:inInputData description:inPacketDescriptions];
}
//返回值没用到
-(OSStatus)enQueueBuffer
{
    
    if (!_queueExist) {
        return -1;
    }
    
    OSStatus err = noErr;
    _inuse[_fillBufferIndex] = true;
    AudioQueueBufferRef fillBuf = _audioQueueBuffer[_fillBufferIndex];
    fillBuf->mAudioDataByteSize = _bytesFilled;
    err = AudioQueueEnqueueBuffer(_audioQueue, fillBuf, _packetsFilled, _packetDescs);
    if (err) {
        NSLog(@"AudioQueueEnqueueBuffer err %d",(int)err);
        return err;
    }
    
    [self StartQueueIfNeeded];
    return err;
}

-(void)waitForFreeBuffer{
    if (++_fillBufferIndex >= kNumAQBufs){
        _fillBufferIndex = 0;
    }
    _bytesFilled = 0;
    _packetsFilled = 0;
    
    printf("->lock\n");
    pthread_mutex_lock(&_mutex);
    while (_inuse[_fillBufferIndex]) {
        printf("WAITING ........\n");
        pthread_cond_wait(&_cond, &_mutex);
    }
    pthread_mutex_unlock(&_mutex);
    printf("<-unlock\n");
}

-(OSStatus)StartQueueIfNeeded{
    OSStatus err = noErr;
    if (!_started) {
        err = AudioQueueStart(_audioQueue, NULL);
        if (err) {
            NSLog(@"AudioQueueStart err %d",(int)err);
            return err;
        }
        _started = true;
        printf("started\n");
    }
    return err;
}

void MyAudioQueueOutputCallback(void *inClientData, AudioQueueRef inAQ, AudioQueueBufferRef	inBuffer)
{
    AACAudioDec *aacDec = (__bridge AACAudioDec *)inClientData;
    [aacDec findQueueBufferAndSignal:inBuffer];
    
}

-(void)findQueueBufferAndSignal:(AudioQueueBufferRef)buffer{
    int bufIndex = -1;
    for (unsigned int i = 0; i < kNumAQBufs; ++i) {
        if (buffer == _audioQueueBuffer[i]){
            bufIndex = i;
            break;
        }
    }
    if (bufIndex!=-1) {
        pthread_mutex_lock(&_mutex);
        _inuse[bufIndex] = false;
        pthread_cond_signal(&_cond);
        pthread_mutex_unlock(&_mutex);
    }
}

void MyAudioQueueIsRunningCallback(void*inClientData,
                                   AudioQueueRef inAQ,
                                   AudioQueuePropertyID	inID)
{
    UInt32 running;
    UInt32 size;
    OSStatus err = AudioQueueGetProperty(inAQ, kAudioQueueProperty_IsRunning, &running, &size);
    if (err) {
        NSLog(@"kAudioQueueProperty_IsRunning err %d",(int)err);//有错误的后续处理
        return;
    }
}
-(void)clearStatus{
    //cleanup
//    free(_bufTag);
//    _bufTag = NULL;
    //    pthread_mutex_destroy(&_mutex);
    //    pthread_cond_destroy(&_cond);
    
    //解码完一帧,重置状态不会对解码有影响 或者放到初始化队列中
    _started = false;
    _bytesFilled = 0;
    _packetsFilled = 0;
    _fillBufferIndex = 0;
    for (int i = 0; i<kNumAQBufs; i++) {
        _inuse[i] = false;
    }
    
}
-(void)stopStreamAndQueue{
    NSLog(@"定时器 执行stop---------------");
    OSStatus err = AudioQueueFlush(_audioQueue);
    if (err) {
        NSLog(@"AudioQueueFlush err %d",(int)err);
        return ;
    }
    err = AudioQueueStop(_audioQueue, false);
    if ((int)err) {
        NSLog(@"AudioQueueStop err %d",(int)err);
        return ;
    }
    
    //这些如何清？ 或者 这些使用前要覆盖的 把索引置0即可
//    AudioQueueBufferRef _audioQueueBuffer[kNumAQBufs];
//    AudioStreamPacketDescription _packetDescs[kAQMaxPacketDescs];
    
    err = AudioFileStreamClose(_audioFileStream);
    err = AudioQueueDispose(_audioQueue, false);
    _audioFileStream = NULL;
    _audioQueue = NULL;
    NSLog(@"定时器 执行stop     结束---------------");
}


#pragma mark - 解码
-(void)decode:(UInt32)bytesRecvd buffer:(char*)buf{
    if (!_audioFileStream) {
        [self initAudioFileStream];
    }
    
    if (!_queueExist) {
        return;
    }
    _dataPlayOver = false;
    
    memset(_bufTag, 0, sizeof(char) * 1024);
    memcpy(_bufTag, buf, bytesRecvd);
    //执行stop后 buftag没有初始化 为空 崩溃
    OSStatus err = AudioFileStreamParseBytes(_audioFileStream, bytesRecvd, _bufTag, 0);
    if (err) {
        NSLog(@"AudioFileStreamParseBytes err %d",(int)err);
        return;
    }
    _dataPlayOver = true;
    return;
}
-(void)audioqueueReset{
    NSLog(@"定时器---------------------");
    if (_queueExist && _started) {
        if (_dataPlayOver) {//确定解码完一帧
            _queueExist = false;//以下避免 stop一半 又开始解码崩溃
//            [self clearStatus];
            [self stopStreamAndQueue];
        }
    }
}
@end
